/**
 *  EKology - An Ecosystem Simulation Framework
 *  Copyright (C) 2008  Aarón Tavío - aaron.tavio at gmail.com - ULPGC
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */

package fungusEater;

import net.ekology.ekosystem.datatypes.*;

import java.util.HashSet;
import java.util.ArrayList;


/**
 * @author Aarón Tavío - aaron.tavio at gmail.com
 * @version 1.0.0 - 20081019-1700
 */
public class GeneticOperators {
    public static Genome crossover(Genome oG1, Genome oG2) {
        return crossover(oG1,oG2,1);
    }
    
    public static Genome crossover(Genome oG1, Genome oG2, int iPoints) {
        Genome oResult;
        boolean bUseFirstSource;
        HashSet<Integer> hsPoints;
        int iGeneCount;
        Gene[] aG1,aG2;
        
        oResult = new Genome();
        iGeneCount = oG1.length();
        if (oG1.length() == oG2.length() && iPoints < iGeneCount) {
            bUseFirstSource = true;
            hsPoints = getRandomPoints(iPoints,iGeneCount);
            
            aG1 = oG1.getGenes();
            aG2 = oG2.getGenes();
            for(int i=0; i < iGeneCount; i++) {
                if (hsPoints.contains(i)) bUseFirstSource = !bUseFirstSource;
                oResult.addGene((bUseFirstSource)?aG1[i]:aG2[i]);
            }
        }
        
        return oResult;
    }
    
    public static Genome mutation(Genome oGenome) {
        return mutation(oGenome,1,1);
    }
    
    
    public static Genome mutation(Genome oGenome, double dProbability) {
        return mutation(oGenome,1,dProbability);
    }
    
    /**
     * Solo muta features
     * 
     * @param oGenome
     * @param iRounds
     * @param dProbability the probability a single gene has to mutate in each round
     * @return
     */
    public static Genome mutation(Genome oGenome, int iRounds, double dProbability) {
        ArrayList<Gene> alFeatures;
        Gene oGene;
        Gene[] aGene;
        int iMutationPosition;
        
        alFeatures = new ArrayList<Gene>();
        aGene = oGenome.getGenes();
        for(int i=0; i<aGene.length; i++) {
            oGene = aGene[i];
            if (oGene instanceof Feature) alFeatures.add(oGene);
        }
        
        if (iRounds > alFeatures.size()) iRounds = alFeatures.size();
        
        for(int i=0; i < iRounds; i++) {
            if (Math.random() < dProbability) {
                iMutationPosition = (int) Math.floor(Math.random()*alFeatures.size());
                oGene = alFeatures.get(iMutationPosition);
                alFeatures.remove(iMutationPosition);

                mutateFeature((Feature) oGene);
            }
        }
        
        return oGenome;
    }
    
    private static HashSet getRandomPoints(int iPoints,int iUpperLimit) {
        HashSet hsPoints;
        
        iUpperLimit--;
        hsPoints = new HashSet<Integer>(iPoints);
        while (hsPoints.size() < iPoints) {
            hsPoints.add(Math.floor(Math.random()*iUpperLimit)+1);
        }
        
        return hsPoints;
    }
    
    private static void mutateFeature(Feature oFeature) {
        double dMaxCapacity;
        double dQuality;
        
        if (oFeature.sName.equals("dMaxCapacity")) {
            dMaxCapacity = Double.parseDouble(oFeature.sValue);
            dMaxCapacity = dMaxCapacity + FungusEaterHelper.getRandomValue(Config.FE_MAX_CAPACITY_DEV_LOWER_LIMIT, Config.FE_MAX_CAPACITY_DEV_UPPER_LIMIT);
            oFeature.sValue = Double.toString(dMaxCapacity);
        }
        else if (oFeature.sName.equals("oFoodType") || oFeature.sName.equals("oType")) oFeature.sValue = FungusEaterHelper.getRandomFungusType().toString();
        else if (oFeature.sName.equals("dQuality")) {
            dQuality = Double.parseDouble(oFeature.sValue);
            dQuality = dQuality + FungusEaterHelper.getRandomValue(Config.F_QUALITY_DEV_LOWER_LIMIT, Config.F_QUALITY_DEV_UPPER_LIMIT);
            oFeature.sValue = Double.toString(dQuality);
        }

    }
    

}
